#include "ABCFile.h"
#include "swf/SWFInputStream.h"
#include <iostream>
#include <assert.h>

void ABCFile::setData( char* data, size_t len )
{
	_stream = new SWFInputStream(data, len);
}

void ABCFile::analyze()
{
	if (_isAnalyzed)
		return;

	_isAnalyzed = true;

	this->minorVersion = _stream->readUI16();
	this->majorVersion = _stream->readUI16();
	this->poolInfo = new CpoolInfo();
	this->poolInfo->read(_stream);
	//////////////////////////////////////////////////////////////////////////
	//method info
	{
		UI30 method_count = _stream->readU30();
		if (method_count > 0)
		{
			for (unsigned int mi = 0; mi < method_count; ++mi)
			{
				MethodInfo* methodInfo = new MethodInfo();
				methodInfo->setABCFile(this);
				methodInfo->read(_stream);
				this->methodInfoVec.push_back(methodInfo);
			}
		}
	}
	//////////////////////////////////////////////////////////////////////////
	UI30 metadata_count = _stream->readU30();
	for (unsigned int i = 0; i < metadata_count; ++i)
	{
		MetadataInfo* metadata = new MetadataInfo();
		metadata->name = _stream->readU30();
		UI30 count = _stream->readU30();
		for (unsigned int j = 0; j < count; ++j)
		{
			MetadataItem* item = new MetadataItem();
			item->key = _stream->readU30();
			item->value = _stream->readU30();
			metadata->itemVec.push_back(item);
		}
		this->metadataInfoVec.push_back(metadata);
	}
	//////////////////////////////////////////////////////////////////////////
	//instance_info
	UI30 class_count = _stream->readU30();
	this->readInstanceInfoVec(_stream, class_count);
	this->readClassInfoVec(_stream, class_count);
	//////////////////////////////////////////////////////////////////////////
	this->readScriptInfoVec(_stream);
	this->readMethodBodyInfoVec(_stream);
}

ABCFile::ABCFile()
	: poolInfo(nullptr)
	, _stream(nullptr)
	, _isAnalyzed(false)
{

}

ABCFile::~ABCFile()
{
	if (_stream)
		delete _stream;
	if (poolInfo)
		delete poolInfo;
	for (MethodInfo* info : methodInfoVec)
		delete info;
	for (MetadataInfo* info : metadataInfoVec)
		delete info;
}

void ABCFile::readInstanceInfoVec( SWFInputStream* stream, UI30 class_count )
{
	for (unsigned int i = 0; i < class_count; ++i)
	{
		InstanceInfo* instanceInfo = new InstanceInfo();
		this->instanceInfoVec.push_back(instanceInfo);
		instanceInfo->setABCFile(this);
		instanceInfo->name = stream->readU30();
		instanceInfo->superName = stream->readU30();
		instanceInfo->flags = stream->readU8();
		if ((instanceInfo->flags & CONSTANT_ClassProtectedNs) == CONSTANT_ClassProtectedNs)
			instanceInfo->protectedNs = stream->readU30();
		UI30 intrf_count = stream->readU30();
		for (unsigned int j = 0; j < intrf_count; ++j)
		{
			instanceInfo->interfaceVec.push_back(stream->readU30());
		}
		instanceInfo->iinit = stream->readU30();
		TraitInfo::readTraitInfoVec(instanceInfo->traitInfoVec, stream);
	}
}

void ABCFile::readClassInfoVec( SWFInputStream* stream, UI30 class_count )
{
	for (unsigned int i = 0; i < class_count; ++i)
	{
		ClassInfo* ci = new ClassInfo();
		this->classInfoVec.push_back(ci);

		ci->cinit = stream->readU30();
		TraitInfo::readTraitInfoVec(ci->traitInfoVec, stream);
	}
}

void ABCFile::readScriptInfoVec( SWFInputStream* stream )
{
	UI30 count = stream->readU30();
	for (unsigned int i = 0; i < count; ++i)
	{
		ScriptInfo* si = new ScriptInfo();
		si->init = stream->readU30();
		TraitInfo::readTraitInfoVec(si->traitInfoVec, stream);
	}
}

void ABCFile::readMethodBodyInfoVec( SWFInputStream* stream )
{
	UI30 count = stream->readU30();
	for (unsigned int i = 0; i < count; ++i)
	{
		MethodBodyInfo* mbi = new MethodBodyInfo();
		this->methodBodyInfoVec.push_back(mbi);
		mbi->setABCFile(this);
		mbi->method = stream->readU30();
		mbi->maxStack = stream->readU30();
		mbi->localCount = stream->readU30();
		mbi->initScopeDepth = stream->readU30();
		mbi->maxScopeDepth = stream->readU30();
		mbi->codeLength = stream->readU30();
		mbi->code = (char*) malloc(mbi->codeLength);
		stream->readBytes(mbi->code, mbi->codeLength);
		UI30 exception_count = stream->readU30();
		for (unsigned int e = 0; e < exception_count; ++e)
		{
			UI30 from = stream->readU30();
			UI30 to = stream->readU30();
			UI30 target = stream->readU30();
			UI30 exc_type = stream->readU30();
			UI30 var_name = stream->readU30();
		}

		TraitInfo::readTraitInfoVec(mbi->traitInfoVec, stream);
	}
}

MethodBodyInfo* ABCFile::getMethodBodyInfo( int methodInfoIndex )
{
	for (MethodBodyInfo* mb : methodBodyInfoVec)
	{
		if (mb->method == methodInfoIndex)
		{
			return mb;
		}
	}
	return nullptr;
}


void CpoolInfo::read( SWFInputStream* streamPtr )
{
	this->readIntegerVec(streamPtr);
	this->readUintVec(streamPtr);
	this->readDoubleVec(streamPtr);
	this->readStringVec(streamPtr);
	this->readNamespaceInfoVec(streamPtr);
	this->readNamespaceSetInfoVec(streamPtr);
	this->readMultinameInfoVec(streamPtr);
}

void CpoolInfo::readIntegerVec( SWFInputStream* stream )
{
	UI30 int_count = stream->readU30();
	this->intVec.push_back(0);
	if (int_count > 0)
	{
		for (unsigned int i = 0; i < int_count - 1; ++i)
		{
			this->intVec.push_back(stream->readS32());
		}
	}
}

void CpoolInfo::readUintVec( SWFInputStream* stream )
{
	UI30 uint_count = stream->readU30();
	this->uintVec.push_back(0);
	if (uint_count > 0)
	{
		for (unsigned int i = 0; i < uint_count - 1; ++i)
		{
			UI32 u = stream->readS32();
			this->uintVec.push_back(u);
		}
	}
}

void CpoolInfo::readDoubleVec( SWFInputStream* stream )
{
	UI30 double_count = stream->readU30();
	this->doubleVec.push_back(0);
	if (double_count > 0)
	{
		for (unsigned int i = 0; i < double_count - 1; ++i)
		{
			DOUBLE d64 = stream->readDouble();
			this->doubleVec.push_back(d64);
		}
	}
}

void CpoolInfo::readStringVec( SWFInputStream* stream )
{
	UI30 string_count = stream->readU30();
	this->stringVec.push_back("*");
	if (string_count > 0)
	{
		for (unsigned int i = 0; i < string_count - 1; ++i)
		{
			std::string s = stream->readUTFString();
			this->stringVec.push_back(s);
		}
	}
}

void CpoolInfo::readNamespaceInfoVec( SWFInputStream* stream )
{
	//namespace
	UI30 namespace_count = stream->readU30();
	//Namespace("*")
	NamespaceInfo* defaultNS = new NamespaceInfo();
	defaultNS->name = 0;
	defaultNS->kind = (NamespaceKind) 0;
	this->namespaceInfoVec.push_back(defaultNS);

	if (namespace_count > 0)
	{
		for (unsigned int i = 0; i < namespace_count - 1; ++i)
		{
			UI8 kind = stream->readU8();
			UI30 nameIdx = stream->readU30();
			NamespaceInfo* ns = new NamespaceInfo();
			ns->setCpool(this);
			ns->kind = (NamespaceKind) kind;
			ns->name = nameIdx;
			this->namespaceInfoVec.push_back(ns);
		}
	}
}

void CpoolInfo::readNamespaceSetInfoVec( SWFInputStream* stream )
{
	//namespace set
	UI30 ns_set_count = stream->readU30();
	if (ns_set_count > 0)
	{
		for (unsigned int i = 0; i < ns_set_count - 1; ++i)
		{
			NamespaceSetInfo* nss = new NamespaceSetInfo();
			nss->setCpool(this);

			UI30 count = stream->readU30();
			for (unsigned int j = 0; j < count; ++j)
			{
				nss->ns.push_back(stream->readU30());
			}

			this->namespaceSetInfoVec.push_back(nss);
		}
	}
}

void CpoolInfo::readMultinameInfoVec( SWFInputStream* stream )
{
	UI30 multiname_count = stream->readU30();
	if (multiname_count > 0)
	{
		for (unsigned int i = 0; i < multiname_count - 1; ++i)
		{
			MultinameInfo* multiname = nullptr;
			MultinameKind kind = (MultinameKind) stream->readU8();
			switch (kind)
			{
			case MultinameKind::QName:
			case MultinameKind::QNameA:
				{
					QName* qn = new QName();
					qn->ns = stream->readU30();
					qn->name = stream->readU30();

					multiname = qn;
				}
				break;
			case MultinameKind::RTQName:
			case MultinameKind::RTQNameA:
				{
					RTQName* rtqname = new RTQName();
					rtqname->name = stream->readU30();

					multiname = rtqname;
				}
				break;
			case MultinameKind::RTQNameL:
			case MultinameKind::RTQNameLA:
				{
					multiname = new RTQNameL();
				}
				break;
			case MultinameKind::Multiname:
			case MultinameKind::MultinameA:
				{
					Multiname* mn = new Multiname();
					mn->name = stream->readU30();
					mn->nss = stream->readU30();

					multiname = mn;
				}
				break;
			case MultinameKind::MultinameL:
			case MultinameKind::MultinameLA:
				{
					MultinameL* mnl = new MultinameL();
					mnl->nss = stream->readU30();

					multiname = mnl;
				}
				break;
			case MultinameKind::TypeName:
				{
					TypeName* tn = new TypeName();
					tn->name = stream->readU30();
					tn->count = stream->readU30();        //must 1
					for (unsigned int jj = 0; jj < tn->count; ++jj)
					{
						tn->typeVec.push_back(stream->readU30());
					}

					multiname = tn;
				}
				break;
			default:
				assert(true);
				break;
			}

			multiname->kind = kind;
			multiname->setCpool(this);
			this->multinameInfoVec.push_back(multiname);
		}
	}
}

CpoolInfo::CpoolInfo()
{

}

CpoolInfo::~CpoolInfo()
{
	for (auto v : namespaceInfoVec)
		delete v;
	for (auto v : namespaceSetInfoVec)
		delete v;
	for (auto v : multinameInfoVec)
		delete v;
}

SI32 CpoolInfo::getInt( UI32 index ) const
{
	return intVec[index];
}

UI32 CpoolInfo::getUint( UI32 index ) const
{
	return uintVec[index];
}

DOUBLE CpoolInfo::getDouble( UI32 index ) const
{
	return doubleVec[index];
}

std::string CpoolInfo::getString( UI32 index ) const
{
	return stringVec[index];
}

NamespaceInfo* CpoolInfo::getNamespaceInfo( UI32 index ) const
{
	return namespaceInfoVec[index];
}

NamespaceSetInfo* CpoolInfo::getNamespaceSetInfo( UI32 index ) const
{
	return this->namespaceSetInfoVec[index];
}

MultinameInfo* CpoolInfo::getMultinameInfo( UI32 index ) const
{
	return multinameInfoVec[index - 1];
}

void MethodInfo::read( SWFInputStream* stream )
{
	UI30 paramCount = stream->readU30();
	this->returnType = stream->readU30();
	//param types
	for (unsigned int i = 0; i < paramCount; ++i)
	{
		MethodParam* param = new MethodParam();
		param->type = stream->readU30();
		this->paramVec.push_back(param);
	}
	this->name = stream->readU30();
	this->flags = stream->readU8();
	//param default values
	if (this->hasOptional())
	{
		UI30 option_count = stream->readU30();
		for (unsigned int i = 0; i < option_count; ++i)
		{
			MethodParam* param = this->paramVec[i + (paramCount - option_count)];
			param->hasDefaultValue = true;
			param->defaultVal = stream->readU30();
			param->defaultKind = (ConstantKind) stream->readU8();
		}
	}
	//param name
	if (this->hasParamNames())
	{
		for (unsigned int i = 0; i < paramCount; ++i)
		{
			MethodParam* param = this->paramVec[i];
			param->name = stream->readU30();
			param->hasName = true;
		}
	}
}

MethodInfo::~MethodInfo()
{
	for (auto param : paramVec)
		delete param;
}

std::string MethodInfo::toString() const
{
	std::string str = _abcFile->poolInfo->getString(this->name);
	str += "(";
	for (MethodParam* p : this->paramVec)
	{
		std::string name = "param";
		if (p->hasName)
			name = _abcFile->poolInfo->getString(p->name);
		name += ":";

		if (p->type > 0)
		{
			MultinameInfo* mn = _abcFile->poolInfo->getMultinameInfo(p->type);
			name += mn->toString();
		}
		name += ", ";
		str += name;
	}
	str += "):";
	if (returnType > 0)
	{
		MultinameInfo* mn = _abcFile->poolInfo->getMultinameInfo(returnType);
		str += mn->toString();
	}
	return str;
}

MetadataInfo::MetadataInfo()
{

}

MetadataInfo::~MetadataInfo()
{
	for (MetadataItem* item : itemVec)
		delete item;
}

void TraitInfo::readTraitInfoVec( std::vector<TraitInfo*>& traitInfoVec, SWFInputStream* stream )
{
	UI30 trait_count = stream->readU30();
	for (unsigned int j = 0; j < trait_count; ++j)
	{
		TraitInfo* ti = nullptr;

		UI30 name = stream->readU30();
		UI8 kind = stream->readU8();

		UI8 traitType = kind & 0xF;
		UI8 traitAttr = kind >> 4;
		switch (traitType)
		{
		case TraitKind::Slot:
		case TraitKind::Const:
			{
				TraitSlot* ts = new TraitSlot();

				ts->slotId = stream->readU30();
				ts->name = stream->readU30();
				ts->vindex = stream->readU30();
				if (ts->vindex != 0)
					ts->vkind  = stream->readU8();
				else
					ts->vkind = 0;

				ti = ts;
			}
			break;
		case TraitKind::Class:
			{
				TraitClass* tc = new TraitClass();
				tc->slotId = stream->readU30();
				tc->classIndex = stream->readU30();

				ti = tc;
			}
			break;
		case TraitKind::Function:
			{
				TraitFunction* tf = new TraitFunction();
				tf->slotId = stream->readU30();
				tf->functionIndex = stream->readU30();

				ti = tf;
			}
			break;
		case TraitKind::Method:
		case TraitKind::Getter:
		case TraitKind::Setter:
			{
				TraitMethod* tm = new TraitMethod();
				tm->dispId = stream->readU30();
				tm->methodIndex = stream->readU30();

				ti = tm;
			}
			break;
		default:
			break;
		}

		ti->name = name;
		ti->kind = (TraitKind) traitType;
		ti->attr = traitAttr;
		//metadata
		if (ti->hasMetadata())
		{
			UI30 metadata_count = stream->readU30();
			for (unsigned int k = 0; k < metadata_count; ++k)
			{
				UI30 idx = stream->readU30();
				ti->metadataVec.push_back(idx);
			}
		}
		traitInfoVec.push_back(ti);
	}
}

MethodBodyInfo::MethodBodyInfo()
	: code(nullptr)
{

}

MethodBodyInfo::~MethodBodyInfo()
{
	if (code)
		free(code);
}

MethodParam::MethodParam()
	: hasDefaultValue(false)
	, hasName(false)
	, name(0)
	, defaultVal(0)
{

}

std::string QName::toString() const 
{
	return _cpool->getString(name);
}
